﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AutoMapper;
using YuYuKiCms.Bl.Interface;
using YuYuKiCms.Dal.Interface;
using YuYuKiCms.Dal.Model;
using YuYuKiCms.Framework;
using YuYuKiCms.Framework.Core.Extention;
using YuYuKiCms.Framework.Interface;
using YuYuKiCms.Model;

namespace YuYuKiCms.Bl.Core
{
    internal class ThemeBL : IThemeBL
    {
        #region Property

        private readonly IThemeRepository _themeRepository;
        private readonly IUtil _util;

        #endregion

        #region Constructor

        public ThemeBL(IThemeRepository themeRepository, IUtil util)
        {
            _themeRepository = themeRepository;
            _util = util;
        }

        #endregion

        #region Method

        /// <summary>
        /// Invalid all theme except current
        /// Find all theme
        /// </summary>
        /// <returns></returns>
        public List<ThemeVm> GetAvailableThemes()
        {
            var result = new List<ThemeVm>();

            // Invalid all themes for avoiding deleted theme witouth uninstall it
            // execept Default
            _themeRepository.InvalidAllThemeExceptCurrentAndDefault();

            // Check well installed theme
            var themePath = _util.GetThemeFolder();
            var dir = Directory.CreateDirectory(themePath);

            // For every Theme folder found, search index.htm or index.html
            foreach (var fileInfo in dir.GetFiles("index.htm?", SearchOption.AllDirectories))
            {
                // Get last part of path to index.htm for getting something like
                // Artisteer/index.htm
                var path = fileInfo.FullName.Substring(themePath.Length + 1);
                var index = path.IndexOf('\\');
                var themeName = index != -1 ? path.Substring(0, index) : "Unknow";

                // Check if the theme already exists
                var theme = GeThemeFromPath(path) ?? new ThemeVm
                {
                    Id = Constant.BaseId,
                    Path = path,
                    PathView = string.Format("~/Views/Shared/{0}.cshtml", themeName),
                    Name = themeName
                };

                if (!fileInfo.LastWriteTimeUtc.AreEqual(theme.LastUpdate.ToUniversalTime()))
                {
                    // It's a new theme or the theme was updated
                    InsertOrUpdate(theme);
                }

                result.Add(Mapper.Map<ThemeVm>(theme));
            }

            return result;
        }

        /// <summary>
        ///     Get the theme path
        /// </summary>
        /// <returns></returns>
        public string GeThemePath()
        {
            return "~/Views/Shared/Artisteer.cshtml";
        }

        /// <summary>
        ///     Retrieve content of selected theme
        ///     Replace content of GetThemePath return value
        /// </summary>
        /// <param name="themeId"></param>
        /// <param name="themePath"></param>
        public void SetTheme(int themeId, string themePath)
        {
            var theme = _themeRepository.GetId(themeId);

            File.WriteAllText(themePath, theme.PageData, Encoding.UTF8);
        }

        #endregion

        #region Method internal

        internal ThemeVm GeThemeFromPath(string path)
        {
            return Mapper.Map<ThemeVm>(_themeRepository.GeThemeFromPath(path));
        }

        /// <param name="contentFile"></param>
        /// <param name="regEx"></param>
        internal IEnumerable<GroupCollection> GetFromRegEx(string contentFile, string regEx)
        {
            var regexObj = new Regex(regEx, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            var allMatchResults = regexObj.Matches(contentFile);
            return allMatchResults.OfType<Match>().Select(m => m.Groups);
        }

        internal string GetContent(string file)
        {
            // Get the content of html page
            return File.ReadAllText(file, Encoding.UTF8);
            //var content = File.ReadAllText(file);//.Replace("\r", "").Replace("\n", "");
            //return Regex.Replace(content, "(.*?)[ ]{2,}(.*?)", "$1$2", RegexOptions.IgnoreCase);
        }

        internal int InsertOrUpdate(ThemeVm theme)
        {
            var path = Path.Combine(_util.GetThemeFolder(), theme.Path);
            // Set valid
            theme.IsPathValid = File.Exists(path);

            if (theme.IsPathValid)
            {
                theme.PageData = CreateThemeFromPath(path);
                theme.LastUpdate = new FileInfo(path).LastWriteTimeUtc;
            }

            if (theme.Id == Constant.BaseId)
            {
                // Add a new theme
                var themeNew = Mapper.Map<Theme>(theme);
                _themeRepository.InsertOrUpdate(themeNew, themeNew.Id);
                theme.Id = themeNew.Id;
            }

            return theme.Id;
        }

        /// <summary>
        ///     replace all placeholders like ##...## by ViewBag.... or RenderBody
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        internal string InsertPlaceholder(string content)
        {
            // Get All placeholders
            var placeholders = GetAllPlaceholders(content);

            // For every duplicate placeholder, Add Index
            var dup = placeholders.GroupBy(f => f.ToLower())
                .Where(g => g.Count() > 1)
                .ToList();

            dup.ForEach(g => g.Select((n, i) => new { placeholder = n, index = i }).ToList().ForEach(f =>
            {
                // We add the index just after the placeholder name so before the 2 "#"
                content = content.Insert(
                    content.IndexOf(f.placeholder, StringComparison.Ordinal) + f.placeholder.Length - 2,
                    f.index.ToString(CultureInfo.InvariantCulture));
            }));

            // Retrieve again all placeholders but now there no duplicate placeholder
            placeholders = GetAllPlaceholders(content);

            // Format all placeholder in PascalCase
            foreach (var placeholder in placeholders)
            {
                var ti = new CultureInfo("fr-FR", false).TextInfo;
                content = content.Replace(placeholder, string.Format("##{0}##", ti.ToTitleCase(placeholder.Trim('#'))));
            }

            // Replace Placeholder
            content = Regex.Replace(content, @"##(\w+)##", "@ViewData[\"${1}\"]", RegexOptions.IgnoreCase);

            return content;
        }

        internal List<string> GetAllPlaceholders(string content)
        {
            return GetFromRegEx(content, @"##\w+##")
                .Select(m => m[0].Value).ToList();
        }

        internal string GetThemePath(string themePath)
        {
            const string pattern2 = @"\index.html";
            themePath = themePath.Substring(_util.GetThemeFolder().Length + 1);
            var index = themePath.IndexOf(pattern2, StringComparison.InvariantCultureIgnoreCase);
            themePath = index != -1 ? themePath.Remove(index) : "";
            themePath = "~/Themes/" + themePath.Replace(@"\", "/");
            return themePath.Trim(new[] { '/' });
        }

        internal string CreateThemeFromPath(string themePath)
        {
            var content = string.Empty;
            if (File.Exists(themePath))
            {
                // Get the content of html page
                content = GetContent(themePath);

                // Add RenderBody
                content = Regex.Replace(content, "##body##",
                    "@RenderBody()", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);

                // Insert placeholders
                content = InsertPlaceholder(content);

                // Replace Menu with engine system
                content = InsertMenu(content);

                // Adapt path
                content = AdaptPath(themePath, content);
            }

            return content;
        }

        internal string AdaptPath(string themePath, string content)
        {
            return Regex.Replace(content, @"(src|href)=""(?!http://)\.?/?(.*?)""",
                "${1}=\"@Href(\"" + GetThemePath(themePath) + "/${2}\")\"",
                RegexOptions.Singleline | RegexOptions.IgnoreCase);
        }

        internal static string InsertMenu(string content)
        {
            return Regex.Replace(content, "<nav(.*?)>(.*?)</nav>",
                "<nav${1}>@Html.Partial(\"Menu\")</nav>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
        }

        #endregion
    }
}